/*
 * Anything: A flexible and easy to use data structure that combines the 
 * characteristics of a list and a hashmap into one data structure.
 * 
 * Based on a paper by Peter Sommerlad and Marcel Rueedi on 
 * 'Do-it-yourself reflection' entered at EuroPLOP 1998.
 * 
 * This library was written completely from scratch and is based only 
 * in spirit on the original Java Anything implementation written by 
 * Andre Weinand/Marcel Rueedi in 1997/99 and enhanced by Stefan Tramm 
 * in 2006.
 * 
 * Copyright (c) 2008 Thomas Marti and others. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package any.impl.util;

import java.lang.Thread.State;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;

public class Utils {

	/**
	 * Your classic, gold-standard static equals method 
	 * that takes <tt>null</tt> into account.
	 * 
	 * @param obj1 The first object, may be <code>null</code>
	 * @param obj2 The second object, may be <code>null</code>
	 * @return <tt>true</tt> if both objects are equal, 
	 *         <tt>false</tt> otherwise.
	 */
	public static boolean equals(Object obj1, Object obj2) {
		return (obj1 != null ? obj1.equals(obj2) : obj2 == null);
	}
	
	public static Iterator<?> getIterator(final Object values) {
		if (values == null) {
			return Collections.emptyList().iterator();

		} else if (values.getClass().isArray()) {
			return new ArrayIterator(values);
		
		} else if (values instanceof Map<?, ?>) {
			return ((Map<?, ?>)values).entrySet().iterator();
		
		} else if (values instanceof Iterable<?>) {
			return ((Iterable<?>)values).iterator();
		
		} else if (values instanceof Iterator<?>) {
			return (Iterator<?>)values;
		
		} else if (values instanceof Enumeration<?>) {
			return new EnumerationIterator((Enumeration<?>)values);
			
		} else {
			return Collections.singletonList(values).iterator();
		}
	}

	public static boolean isIterableType(Object value) {
		if (value == null) return false;

		Class<?> cls = value.getClass();
		if (cls.isArray() || isInstanceOf(value, Map.class, Iterable.class, Iterator.class, Enumeration.class)) {
			return true;
		}

		return false;
	}

	/**
	 * Estimates the number of elements in the specified 
	 * object.
	 * <p>
	 * Guaranteed to return the <b>exact size or less, 
	 * but never more</b>. This can be used to 
	 * pre-emptively grow data structures, when 
	 * bulk-adding objects.
	 * 
	 * @param values Object to estimate the size.
	 * @return The estimated number of elements.
	 */
	public static int estimateSize(Object values) {
		if (values == null) {
			return 0;
			
		} else if (values instanceof Collection<?>) {
			return ((Collection<?>)values).size();
		
		} else if (values instanceof Map<?, ?>) {
			return ((Map<?, ?>)values).size();
		
		} else if (values.getClass().isArray()) {
			return Array.getLength(values);
		
		} else if (values instanceof Iterator<?> && !((Iterator<?>)values).hasNext()) {
			return 0;
		
		} else if (values instanceof Enumeration<?> && !((Enumeration<?>)values).hasMoreElements()) {
			return 0;
		
		} else if (values instanceof Iterable<?>) {
			return 0;
		
		} else {
			return 1;
		}
	}
	
	public static <T> Iterable<T> wrapIterator(final Iterator<T> iterator) {
		return new BaseIterable<T>() {
			public Iterator<T> iterator() {
				return iterator;
			}
		};
	}

	public static boolean isInstanceOf(Object value, Class<?>... classes) {
		if (value == null || classes == null) {
			return false;
		}
	
		Class<?> cls = value.getClass();
		for (int i = 0; i < classes.length; i++) {
			if (classes[i].isAssignableFrom(cls)) {
				return true;
			}
		}
		return false;
	}	
	
	public static boolean isEnum(Object obj) {
		return obj instanceof Enum<?>;
	}
	
	public static boolean isEnum(Class<?> cls) {
		while(cls != null) {
			if (cls.isEnum()) return true;
			cls = cls.getSuperclass();
		}
		return false;		
	}
	
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getClass(T obj) {
		return (Class<T>)(obj != null ? obj.getClass() : null);
	}
	
	public static String escapeString(String str, boolean escapeSpaces) {
		int len = str.length();
		StringBuilder dst = new StringBuilder(2 * len); // simple assumption to have enough room

		for (int i = 0; i < len; ++i) {
			char c = str.charAt(i);
			int n = c;
			if (c == '\\') {
				dst.append("\\\\");
			} else if (c == '\"') {
				dst.append("\\\"");
			} else if (c == '\'') {
				dst.append("\\\'");
			} else if (c == '\n') {
				dst.append("\\n");
			} else if (c == '\t') {
				dst.append("\\t");
			} else if (c == '\b') {
				dst.append("\\b");
			} else if (c == '\f') {
				dst.append("\\f");
			} else if (c == '\r') {
				dst.append("\\r");
			} else if (c == ' ') {
				if (escapeSpaces) {
					dst.append("\\u0020");
				} else {
					dst.append(' ');
				}
			} else if (n > 32 && n <= 126 || n >= 160 && n <= 254) {
				dst.append(c);
			} else {
				dst.append("\\u");
				String h = Integer.toHexString(n);
				if (h.length() == 2) {
					dst.append("00");
				} else if (h.length() == 1) {
					dst.append("000");
				} else {
					dst.append('0');
				}
				dst.append(h);
			}
		}
		return dst.toString();
	}
	
	private static final class EnumerationIterator implements Iterator<Object> {
		private final Enumeration<?> values;

		public EnumerationIterator(Enumeration<?> values) {
			this.values = values;
		}

		public boolean hasNext() {
		    return values.hasMoreElements();
		}

		public Object next(){
			return values.nextElement();
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}
	}

	private static class ArrayIterator implements Iterator<Object> {
		private transient final Object array;
		private transient int currentIndex = 0;
		
		public ArrayIterator(Object array) {
			this.array = array;
		}
		public boolean hasNext() {
			return currentIndex < Array.getLength(array);
		}
		public Object next() {
			try {
				return Array.get(array, currentIndex++);
			} catch (ArrayIndexOutOfBoundsException e) {
				throw new NoSuchElementException(e.getMessage());
			}
		}
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}

	public static void main(String[] args) {
		System.out.println(isEnum(State.BLOCKED));
		String x = "\u00A2";
		System.out.println(x);
		System.out.println((int)x.charAt(0));
	}
	
}
